#!/usr/bin/python3
import json
import traceback
from threading import BoundedSemaphore, Thread, current_thread, Lock

from socket import socket

from protocol_payload import ProtocolPayload


def protocol_encode(msg):
    msgByte = msg.encode(encoding='utf-8')
    msgLength = len(msgByte)
    msgBytes = bytearray(b'')
    msgBytes.extend(msgLength.to_bytes(4, "big"))
    msgBytes.extend(msgByte)
    return msgBytes


class SocketClient:

    def __init__(self, host, port):
        self._socket_client = socket()
        self.__host = host
        self.__port = port
        self.__write_lock = Lock()
        self.__read_lock = Lock()
        self.__send_call_back = {}

    def connect(self):
        self._socket_client.connect((self.__host, self.__port))

    def send(self, msg: ProtocolPayload, on_success=None, on_error=None):
        try:
            self.__write_lock.acquire()
            print('%s 获得了锁' % current_thread().name)
            self._socket_client.send(protocol_encode(msg.to_json()))
            if on_success is not None:
                self.__send_call_back[msg.cmd_id] = on_success
        except Exception as e:
            if on_error is not None:
                on_error(e)
            traceback.print_exc()
        finally:
            print('%s 释放了了锁' % current_thread().name)
            self.__write_lock.release()

    def message(self, on_error=None):
        # 此处需要处理黏包问题
        try:
            msg: ProtocolPayload = self.recv()
            if msg.cmd_id in self.__send_call_back:
                self.__send_call_back[msg.cmd_id](msg)
                del self.__send_call_back[msg.cmd_id]
                return None
            return msg
        except Exception as e:
            if on_error is not None:
                on_error(e)
            traceback.print_exc()

    def recv(self) -> ProtocolPayload:
        try:
            self.__read_lock.acquire()
            data_length_bytes = self._socket_client.recv(4)
            data_len = int.from_bytes(data_length_bytes, "big")
            msg_bytes = bytearray(b'')
            while len(msg_bytes) < data_len:
                msg_data = self._socket_client.recv(data_len - len(msg_bytes))
                msg_bytes.extend(msg_data)

            if data_len != len(msg_bytes):
                print('data_len=%d msg_len = %d' % (data_len, len(msg_bytes)))
            return ProtocolPayload.parse_object(json.loads(msg_bytes.decode('utf-8')))
        finally:
            self.__read_lock.release()

    def close(self):
        self._socket_client.close()
